package com.maizi.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
//参考网站 :https://blog.csdn.net/wangzhongshun/article/details/122548382
//结论:  提现线程池的参数不是越大越好 最优应该

/**
 * 为了说明合理设置的条件，我们首先确定有以下几个相关参数：
 * 1.tasks，程序每秒需要处理的最大任务数量（假设系统每秒任务数为100~1000）
 * 2.tasktime，单线程处理一个任务所需要的时间（每个任务耗时0.1秒）
 * 3.responsetime，系统允许任务最大的响应时间（每个任务的响应时间不得超过2秒）
 *
 * corePoolSize
 *
 * 每个任务需要tasktime秒处理，则每个线程每秒可处理1/tasktime个任务。系统每秒有tasks个任务需要处理，则需要的线程数为：tasks/(1/tasktime)。
 * 即tasks*tasktime个线程数。假设系统每秒任务数为100到1000之间，每个任务耗时0.1秒，则需要100x0.1至1000x0.1，即10到100个线程。那么corePoolSize应该设置为大于10。
 * 具体数字最好根据8020原则，即80%情况下系统每秒任务数，若系统80%的情况下任务数小于200，最多时为1000，则corePoolSize可设置为20。
 * queueCapacity：任务队列的长度
 *
 * 任务队列的长度要根据核心线程数，以及系统对任务响应时间的要求有关。队列长度可以设置为(corePoolSize/tasktime)responsetime： (20/0.1)2=400，即队列长度可设置为400。
 * 如果队列长度设置过大，会导致任务响应时间过长，如以下写法：
 * LinkedBlockingQueue queue = new LinkedBlockingQueue();
 * 这实际上是将队列长度设置为Integer.MAX_VALUE，将会导致线程数量永远为corePoolSize，再也不会增加，当任务数量陡增时，任务响应时间也将随之陡增。
 * maxPoolSize:最大线程数
 *
 * 当系统负载达到最大值时，核心线程数已无法按时处理完所有任务，这时就需要增加线程。每秒200个任务需要20个线程，那么当每秒达到1000个任务时，则需要(1000-queueCapacity)*(20/200)，即60个线程，可将maxPoolSize设置为60。
 * keepAliveTime:
 *
 * 线程数量只增加不减少也不行。当负载降低时，可减少线程数量，如果一个线程空闲时间达到keepAliveTiime，该线程就退出。默认情况下线程池最少会保持corePoolSize个线程。keepAliveTiime设定值可根据任务峰值持续时间来设定。
 *
 * 以上关于线程数量的计算并没有考虑CPU的情况。若结合CPU的情况，比如，当线程数量达到50时，CPU达到100%，则将maxPoolSize设置为60也不合适，此时若系统负载长时间维持在每秒1000个任务，则超出线程池处理能力，应设法降低每个任务的处理时间(tasktime)。
 */
public class ThreadPoolTest {

    // 任务数
    private static int taskCount = 50;
    // 实际完成任务数
    private static AtomicInteger taskCountExecuted;

    public static void main(String[] args) {
        init();
    }

    private static void init(){
        taskCountExecuted = new AtomicInteger(0);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,        // 核心线程数
                20,    // 最大线程数
                5,        // 非核心线程回收超时时间
                TimeUnit.SECONDS,     // 超时时间单位
                new ArrayBlockingQueue<>(30)      // 任务队列
        );

        System.out.println("任务总数 = [" + taskCount + "]个");
        long start = System.currentTimeMillis();

        for(int i=0; i<taskCount; i++){
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(500);
                        System.out.println("已执行第 [" + taskCountExecuted.addAndGet(1) + "] 个任务");
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            };

            try{
                // 默认拒绝策略会报错
                threadPoolExecutor.execute(runnable);
            }catch (Exception ex){
            	ex.printStackTrace();
                taskCount = threadPoolExecutor.getActiveCount() + threadPoolExecutor.getQueue().size();
            }
        }

        long end = 0;
        while (threadPoolExecutor.getCompletedTaskCount() < taskCount){
            end = System.currentTimeMillis();
        }

        System.out.println("[" + taskCountExecuted + "]个任务执行总耗时 = [" + (end - start) + "]ms");
        threadPoolExecutor.shutdown();
    }
}

